#include "EditLayer.h"
#include <SERender/Drive/Drive.h>
#include <SEngine/Render/BaseRenderer.h>
#include <SECore/CoreApplication.h>
#include <SEEdit/Widget/View.h>
#include <SEEdit/Widget/SceneWidget.h>
#include <SEEdit/Widget/AttributeWidget.h>
#include <SEEdit/Widget/AssetsWidget.h>
#include <SEEdit/Widget/SettingsWidget.h>
#include <SEUi/SEUi.h>
#include <SEEdit/Interactor/EditInteractor.h>
#include <SEngine/Render/Descriptors/SceneDescriptor.h>
#include <SEEdit/Descriptors/SelectActorDescriptor.h>
#include <SECore/Ecs/Scene.h>
#include <SEEdit/Widget/ControlBarWidget.h>
#include <SEEdit/Widget/ProfilerWidget.h>
#include <SEEdit/Widget/MaterialEditorWidget.h>
#include <SEEdit/Widget/MaterialViewWidget.h>
#include <SEEdit/Widget/ConsoleWidget.h>
#include <SEEdit/Widget/AnimationWidget.h>
#include <SEEdit/Widget/AnimationClipWidget.h>
#include <Windows.h>
#include <SEEdit/Descriptors/WidgetsDescriptor.h>
#include <SEngine/Ecs/Components/NameComp.h>
#include <SEngine/Ecs/Components/UuidComp.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SEEdit/EventHandlers/FirstPersonHandler.h>
#include <SEEdit/EventHandlers/OrbitHandler.h>
#include <SEEdit/Descriptors/RenderSettingDescriptor.h>
#include <SEEdit/Widget/RenderPassSettingWidget.h>
#include <SESerializeYAML/ProjectSerializer.h>
#include <SECore/Util.h>
#include <SEDebug/Log/Log.h>
#include <SESerializeYAML/ActorSerializer.h>
#include <SEEdit/Layer/GuideLayer.h>

#include <filesystem>
#include <SEJsonConfig/Parser.h>

namespace SEngine
{

    void WidgetRigister(Widget & view, WidgetsDescriptor & state, WidgetsDescriptor::WidgetTypes en) {
        view.SignalClose.AddSlot([&state, en]() { state.types = (WidgetsDescriptor::WidgetTypes)(state.types & ~en); return SlotState::Keep; });
        state.SignalChange.AddSlot([&view, en](WidgetsDescriptor::WidgetTypes & type) { view.SetVisiable(type & en); return SlotState::Keep; });
    }

    void EditLayer::Attach()
    {
        m_app.AddDescriptor<SelectActorDescriptor>(std::move(SelectActorDescriptor{}));
        m_app.AddDescriptor<WidgetsDescriptor>(std::move(WidgetsDescriptor{}));
        m_app.AddDescriptor<RenderSettingDescriptor>();
    

        auto & widgetsState = m_app.GetDescriptor<WidgetsDescriptor>()->get();


        auto view = std::make_unique<View>(*this);
        auto sceneManager = std::make_unique<SceneWidget>(*this);
        auto attrManager = std::make_unique<AttributeWidget>(*this);
        auto assetsManager = std::make_unique<AssetsWidget>(*this);
        auto controlBar = std::make_unique<ControlBarWidget>(*this);
        auto profilerManager = std::make_unique<ProfilerWidget>(*this);
        auto materialViewManager = std::make_unique<MaterialViewWidget>(*this);
        auto materialEditor = std::make_unique<MaterialEditorWidget>(*this);
        auto consoleManager = std::make_unique<ConsoleWidget>(*this);
        auto settings = std::make_unique<SettingsWidget>(*this);
        // auto animationManager = std::make_unique<AnimationWidget>(*this);
        // auto animationClipWidget = std::make_unique<AnimationClipWidget>(*this);
        auto renderPassSetting = std::make_unique<RenderPassSettingWidget>(*this);

        auto cameraHandler = std::make_unique<FirstPersonHandler>(*view, view->GetCamera());
        auto & cameraHandlerRef = *cameraHandler;

        sceneManager->SetTitle("Scene");
        sceneManager->SignalFocusActor.AddSlot([&cameraHandlerRef](glm::vec3 target) { cameraHandlerRef.MoveTarget(target); return SlotState::Keep; });
        WidgetRigister(*sceneManager, widgetsState, WidgetsDescriptor::SE_SceneManager);

        view->SetTitle("Scene View");
        view->SetInteractor(std::make_unique<EditInteractor>(*view));
        view->SignalFocusSelectActor.AddSlot([&cameraHandlerRef](glm::vec3 target) { cameraHandlerRef.MoveTarget(target); return SlotState::Keep; });
        view->SignalCameraUpdate.AddSlot([&profilerManager = *profilerManager](Camera & camera) { profilerManager.OnCameraUpdate(camera); return SlotState::Keep; });
        view->SignalCameraUpdate.AddSlot([&settings = *settings](Camera & camera) { settings.OnUpdateCamera(camera); return SlotState::Keep; });
        view->AddEventHandler(std::move(cameraHandler));
        WidgetRigister(*view, widgetsState, WidgetsDescriptor::SE_SceneView);
 
        attrManager->SetTitle("Attribute");
        attrManager->SignalLoadMaterial.AddSlot([&materialEditor = *materialEditor, this](ResRef<Material> material) { 
            materialEditor.LoadMaterial(material); 
            this->FocusWidget(WidgetsDescriptor::SE_MaterialEditor);
            return SlotState::Keep; 
        });
        WidgetRigister(*attrManager, widgetsState, WidgetsDescriptor::SE_AttributeView);

        assetsManager->SetTitle("Assets");
        WidgetRigister(*assetsManager, widgetsState, WidgetsDescriptor::SE_AssetsView);

        controlBar->SetTitle("Control Bar");
        WidgetRigister(*controlBar, widgetsState, WidgetsDescriptor::SE_ControlBar);

        profilerManager->SetTitle("Profiler");
        WidgetRigister(*profilerManager, widgetsState, WidgetsDescriptor::SE_ProfilerView);

        materialViewManager->SetTitle("Material View");
        materialViewManager->AddEventHandler(std::make_unique<OrbitHandler>(*materialViewManager, materialViewManager->GetCamera()));
        WidgetRigister(*materialViewManager, widgetsState, WidgetsDescriptor::SE_MaterialView);

        materialEditor->SetTitle("Material Editor");
        materialEditor->SignalLoadMaterial.AddSlot([&materialViewManager = *materialViewManager](ResRef<Material> material) { materialViewManager.LoadMaterial(material); return SlotState::Keep; });
        WidgetRigister(*materialEditor, widgetsState, WidgetsDescriptor::SE_MaterialEditor);

        consoleManager->SetTitle("Console View");
        WidgetRigister(*consoleManager, widgetsState, WidgetsDescriptor::SE_ConsoleView);

        // animationManager->SetTitle("Animation Control");
        // WidgetRigister(*animationManager, widgetsState, WidgetsDescriptor::SE_AnimationManager);

        settings->SetTitle("Settings");

        // animationClipWidget->SetTitle("Animation Clip");
        // WidgetRigister(*animationClipWidget, widgetsState, WidgetsDescriptor::SE_AnimationClip);
        
        renderPassSetting->SetTitle("RenderPass Setting");
        renderPassSetting->SignalUpdateUI.AddSlot([&view = *view]() { view.SignalUpdateRenderPassSettingUI.Invoke(); return SlotState::Keep; });


        this->AddWidget(std::move(view));
        this->AddWidget(std::move(sceneManager));
        this->AddWidget(std::move(attrManager));
        this->AddWidget(std::move(assetsManager));
        this->AddWidget(std::move(controlBar));
        this->AddWidget(std::move(profilerManager));
        this->AddWidget(std::move(materialViewManager));
        this->AddWidget(std::move(materialEditor));
        this->AddWidget(std::move(consoleManager));
        this->AddWidget(std::move(settings));
        // this->AddWidget(std::move(animationManager));
        // this->AddWidget(std::move(animationClipWidget));
        this->AddWidget(std::move(renderPassSetting));

        // 加载项目配置
        LoadProjConfig();
    }

    void EditLayer::Detach()
    {
        // 保存项目配置
        SaveProjConfig();
    }

    void EditLayer::Update(int delta)
    {
        ShowMainMenuBar();
        const ImGuiViewport* viewport = ImGui::GetMainViewport();
        ImGui::SetNextWindowPos(viewport->WorkPos);
        ImGui::SetNextWindowSize(viewport->WorkSize);
        ImGui::SetNextWindowViewport(viewport->ID);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0.0f, 0.0f});
        ImGui::Begin("MainWindow", nullptr, ImGuiWindowFlags_NoTitleBar);
        ImGui::DockSpace(ImGui::GetID("MyDockSpace"));
        ImGui::End();
        ImGui::PopStyleVar(3);
        m_widgets.Foreach([delta](std::string & name, std::unique_ptr<Widget> & widget) {
            widget->Update(delta);

        });
        auto & widgetsState = m_app.GetDescriptor<WidgetsDescriptor>()->get();
        if (widgetsState.types & WidgetsDescriptor::SE_DearImGuiDemo)
            ImGui::ShowDemoWindow();
    }

    void EditLayer::OnEvent(EventPtr event)
    {
        m_widgets.Foreach([event](std::string & name, std::unique_ptr<Widget> & widget) {
            widget->OnEvent(event);
        });
    }

    std::optional<std::reference_wrapper<Actor>> EditLayer::GetSelectActor()
    {
        auto &selectActorDes = m_app.GetDescriptor<SelectActorDescriptor>()->get();
        if (selectActorDes.selectActor) {
            return selectActorDes.selectActor;
        }
        return std::nullopt;
    }

    void EditLayer::DeleteActor(Actor &actor)
    {
        auto &selectActorDes = m_app.GetDescriptor<SelectActorDescriptor>()->get();
        auto & project = m_app.GetDescriptor<Project>()->get();
        if (selectActorDes.selectActor && &actor == &selectActorDes.selectActor->get()) {
            selectActorDes.selectActor.reset();
        }
        project.GetScene()->DestroyActor(actor);
        Log::GetInstance().Info("Delete an Actor");
    }

    Actor &EditLayer::CreateActor()
    {
        auto & project = m_app.GetDescriptor<Project>()->get();
        auto & actor = project.GetScene()->CreateActor();
        actor.AddComponent<NameComp>();
        actor.AddComponent<UuidComp>();
        actor.AddComponent<TransformComp>();
        Log::GetInstance().Info("Create an Actor");
        return actor;
    }

    Actor &EditLayer::CloneActor(Actor &actor)
    {
        auto & project = m_app.GetDescriptor<Project>()->get();
        auto & newActor = project.GetScene()->CreateActor();
        newActor.Deserialize(actor.Serialize());
        actor.RemoveComponent<UuidComp>();
        actor.AddComponent<UuidComp>();
        return newActor;
    }

    void WindowMenuItem(const std::string & name, WidgetsDescriptor::WidgetTypes en, WidgetsDescriptor & type) {
        if (ImGui::MenuItem(name.c_str(), "", type.types & en)) {
            type.types = (WidgetsDescriptor::WidgetTypes)(type.types & en ? type.types & ~en : type.types | en);
            type.SignalChange.Invoke(type.types);
        }
    }

    void EditLayer::ShowMainMenuBar()
    {
        if (ImGui::BeginMainMenuBar()) {
            if (ImGui::BeginMenu("Files")) {
                if (ImGui::MenuItem("Back To Home")) {
                    m_app.SendTask([this](CoreApplication & app) {
                        app.RemoveLayer("EditLayer");
                        app.RemoveLayer("PhysicsLayer");
                        app.RemoveLayer("SceneLayer");
                        app.RemoveLayer("AnimationLayer");
                        Manager::GetInstance().Clear();
                        app.PushFrontLayer("GuideLayer", std::make_unique<GuideLayer>(app));

                    });
                }
                if (ImGui::MenuItem("Save", "Ctrl+S")) {
                    std::string projFilePath = m_app.GetDescriptor<Project>()->get().GetOption().path;
                    SEWriteText(projFilePath, m_app.GetDescriptor<Project>()->get().Serialize());
                }
                if (ImGui::MenuItem("Exit", "Alt+F4")) {
                    std::string projFilePath = m_app.GetDescriptor<Project>()->get().GetOption().path;
                    SEWriteText(projFilePath, m_app.GetDescriptor<Project>()->get().Serialize());
                    m_app.GetEventLoop().Stop();
                }
                ImGui::EndMenu();
            }
            if (ImGui::BeginMenu("Build")) {
                if (ImGui::MenuItem("Build and Run", "")) {
                    std::string projFilePath = m_app.GetDescriptor<Project>()->get().GetOption().path;
                    SEWriteText(projFilePath, m_app.GetDescriptor<Project>()->get().Serialize());
                    ShellExecute(NULL, "open", "SEGamePlay.exe",  m_app.GetDescriptor<Project>()->get().GetOption().path.c_str(), NULL, SW_SHOW);
                }
                ImGui::EndMenu();
            }
            auto & type = m_app.GetDescriptor<WidgetsDescriptor>()->get();
            if (ImGui::BeginMenu("Window")) {
                if (ImGui::BeginMenu("Layout")) {
                    WindowMenuItem("Scene View", WidgetsDescriptor::SE_SceneView, type);
                    WindowMenuItem("Scene", WidgetsDescriptor::SE_SceneManager, type);
                    WindowMenuItem("Attribute", WidgetsDescriptor::SE_AttributeView, type);
                    WindowMenuItem("Assets", WidgetsDescriptor::SE_AssetsView, type);
                    WindowMenuItem("Control Bar", WidgetsDescriptor::SE_ControlBar, type);
                    WindowMenuItem("Profiler", WidgetsDescriptor::SE_ProfilerView, type);
                    WindowMenuItem("Material View", WidgetsDescriptor::SE_MaterialView, type);
                    WindowMenuItem("Material Editor", WidgetsDescriptor::SE_MaterialEditor, type);
                    WindowMenuItem("Console View", WidgetsDescriptor::SE_ConsoleView, type);
                    WindowMenuItem("Animation Manager", WidgetsDescriptor::SE_AnimationManager, type);
                    ImGui::EndMenu();
                }
                ImGui::Separator();
                if (ImGui::MenuItem("Save Layout")) {
                    ImGui::SaveIniSettingsToDisk("imgui.ini");
                }
                ImGui::Separator();
                if (ImGui::MenuItem("Settings")) {
                    static_cast<SettingsWidget&>(*m_widgets.At("Settings")).Popup();
                }
                ImGui::EndMenu();
            }
            if (ImGui::BeginMenu("Help")) {
                WindowMenuItem("Dear ImGui Demo", WidgetsDescriptor::SE_DearImGuiDemo, type);
                ImGui::EndMenu();
            }
            ImGui::EndMainMenuBar();
        }
    }

    void EditLayer::LoadProjConfig()
    {
        auto & proj = m_app.GetDescriptor<Project>()->get();
        std::filesystem::path root(proj.GetOption().path);
        auto projCfg = root.parent_path() / "seproj";
        if (!std::filesystem::exists(projCfg)) {
            SaveProjConfig();
        }
        Parser parser; 
        parser.load(SEReadText(projCfg.string()));
        Json jsonRoot = parser.parse();
        auto & renderSetting = m_app.GetDescriptor<RenderSettingDescriptor>()->get();
        auto & mainCamera = static_cast<View*>(m_widgets.At("Scene View").get())->GetCamera();
        renderSetting.grid = jsonRoot["RenderSetting"]["grid"].asDouble();
        renderSetting.isShowGrid = jsonRoot["RenderSetting"]["isShowGrid"].asBool();
        // mainCamera.SetZNear(jsonRoot["MainCamera"]["zNear"].asDouble());
        // mainCamera.SetZFar(jsonRoot["MainCamera"]["zFar"].asDouble());
        // if (jsonRoot["MainCamera"]["Skybox"].asString() != "Null")
        //     mainCamera.SetSkybox(Manager::GetInstance().Get<TextureHDR>(jsonRoot["MainCamera"]["Skybox"].asString()).value());
        // mainCamera.SetClearColor(glm::vec4{
        //     jsonRoot["MainCamera"]["ClearColor"]["r"].asDouble(),
        //     jsonRoot["MainCamera"]["ClearColor"]["g"].asDouble(),
        //     jsonRoot["MainCamera"]["ClearColor"]["b"].asDouble(),
        //     jsonRoot["MainCamera"]["ClearColor"]["a"].asDouble(),
        // });
    }

    void EditLayer::SaveProjConfig()
    {
        auto & proj = m_app.GetDescriptor<Project>()->get();
        std::filesystem::path root(proj.GetOption().path);
        auto projCfg = root.parent_path() / "seproj";
        auto & renderSetting = m_app.GetDescriptor<RenderSettingDescriptor>()->get();
        auto & mainCamera = static_cast<View*>(m_widgets.At("Scene View").get())->GetCamera();
        Json jsonRoot;
        jsonRoot["RenderSetting"]["grid"] = renderSetting.grid;
        jsonRoot["RenderSetting"]["isShowGrid"] = renderSetting.isShowGrid;
        // jsonRoot["MainCamera"]["Skybox"] = mainCamera.GetSkybox().expired() ? "Null" : mainCamera.GetSkybox().lock()->Name();
        // jsonRoot["MainCamera"]["zNear"] = mainCamera.GetZNear();
        // jsonRoot["MainCamera"]["zFar"] = mainCamera.GetZFar();
        // jsonRoot["MainCamera"]["ClearColor"]["r"] = mainCamera.GetClearColor().r;
        // jsonRoot["MainCamera"]["ClearColor"]["g"] = mainCamera.GetClearColor().g;
        // jsonRoot["MainCamera"]["ClearColor"]["b"] = mainCamera.GetClearColor().b;
        // jsonRoot["MainCamera"]["ClearColor"]["a"] = mainCamera.GetClearColor().a;
        SEWriteText((projCfg).string(), jsonRoot.str());
    }

    void EditLayer::AddWidget(std::unique_ptr<Widget> &&widget)
    {
        m_widgets.EmplaceBack(widget->GetTitle(), std::move(widget));
    }

    void EditLayer::FocusWidget(WidgetsDescriptor::WidgetTypes en)
    {
        auto & type = m_app.GetDescriptor<WidgetsDescriptor>()->get();
        type.types = (WidgetsDescriptor::WidgetTypes)(type.types & ~en);
        type.SignalChange.Invoke(type.types);
        CoreApplication::App().GetEventLoop().SendTask([en, &type](CoreApplication & app) {
            type.types = (WidgetsDescriptor::WidgetTypes)(type.types | en);
            type.SignalChange.Invoke(type.types);
        }, EventLoop<CoreApplication&>::TaskMode::Defer);
    }

} // namespace SEngine

